// Mediator for Total Commander plugins.
#include "totalCommander.h"
#include <pryn/types/archive.h>

// "C:\\Emulation\\Amiga\\disks\\Amberstar\\amber1.adf"

PrynResult totalCommanderFactoryCreate (PrynFactory *factory, PrynComponent **componentPointer)
{
	PrynComponent *component = 0;
	PrynResult result = PrynResult_Success;
	
	result = prynAllocateComponent (factory, &component, 0);
	if (result < 0) goto failure;
	
success:
	if (componentPointer)
		*componentPointer = component;
	
	if (0) { failure:;
	}
	
	return result;
}

PrynResult loadArchive (PrynState *state, TotalCommanderPlugin *plugin, const char *filename, PrynArchive *result)
{
	PrynArchive archive;
	TotalCommanderOpenArchiveData info;
	TotalCommanderHeaderData header;
	char commentBuffer [1024];
	
	prynClearObject (&info);
	info.commentBuffer = commentBuffer;
	info.commentBufferSize = sizeof (commentBuffer);
	info.filename = (char *) filename;
	
	prynPrintLiteral (state, "Here we go\n");
	HANDLE handle = plugin->openArchive (&info);
	if (!handle) return PrynResult_Failure;
	
	prynPrintLiteral (state, "Return: ");
	prynPrintInteger (state, info.openResult);
	prynPrintLiteral (state, "\n");
	
	while (1)
	{
		prynClearObject (&header);
		header.commentBuffer = commentBuffer;
		header.commentBufferSize = sizeof (commentBuffer);
		
		int headerResult = plugin->readHeader (handle, &header);
		
		if (headerResult)
		{
			if (headerResult != TotalCommandResult_EndArchive)
			{
				prynPrintLiteral (state, "Error: ");
				prynPrintInteger (state, headerResult);
			}
			break;
		}
		
		PrynArchiveFile file;
		prynClearObject (&file);
		
		file.name = prynStringDuplicate (state, prynStringFromZMaximum (header.fileName, sizeof (header.fileName)));
		file.size = header.unpackedSize;
		file.archiveSize = header.packedSize ? header.packedSize : header.unpackedSize;
		
		file.modifiedTime = prynCreateTime (
			(header.time >> 25) + 1980,
			(header.time >> 21) & 15,
			(header.time >> 16) & 31,
			(header.time >> 11) & 31,
			(header.time >> 5) & 63,
			(header.time & 31) * 2);
		
		if (header.attributes & TotalCommanderAttribute_ReadOnly) file.flags |= PrynArchiveFileFlags_ReadOnly;
		if (header.attributes & TotalCommanderAttribute_Hidden) file.flags |= PrynArchiveFileFlags_Hidden;
		if (header.attributes & TotalCommanderAttribute_System) file.flags |= PrynArchiveFileFlags_System;
			/*
				TotalCommanderAttribute_ReadOnly = 1, /// Read-only.
	TotalCommanderAttribute_Hidden = 2, /// Hidden.
	TotalCommanderAttribute_System = 4, /// System file.
	TotalCommanderAttribute_VolumeIdentifier = 8, /// Volume identifier file.
	TotalCommanderAttribute_Directory = 0x10, /// Directory.
	TotalCommanderAttribute_Archive = 0x20, /// Archived file.
	TotalCommanderAttribute_Mask = 0x3F, /// Mask of all attributes.
			*/
	/*PrynArchiveFileFlags_None = 0, /// No flags specified.
	PrynArchiveFileFlags_Directory = 1 << 0, /// This file is actually a directory.
	PrynArchiveFileFlags_Hidden = 1 << 1, /// This file should be hidden from normal view.
	PrynArchiveFileFlags_ReadOnly = 1 << 2, /// Writing to the file should not be normally possible.
	PrynArchiveFileFlags_System = 1 << 3, /// The file is important to the operating system.
	PrynArchiveFileFlags_Archive = 1 << 4, /// This is an archive of another file.*/		
		
	/*
char archiveName [260]; /// Name of the archive.
	int flags; /// Appears unused.
	int hostOs; /// Unused.
	int fileCrc; /// 32-bit CRC of the file or zero if there is none.
	int unpackedVersion; /// Unknown.
	int method; /// Packing method?
	int attributes; /// Attributes, bitmask of TotalCommanderAttribute.
	char *commentBuffer; /// (in) Optional comment buffer.
	int commentBufferSize; /// (in) Total size of the comment buffer.
	int commentSize; /// Written size of the comment buffer. 
	int commentState; /// Unknown.	
	*/
		prynPrintFormat (state, "flags=%d host=%d crc=%d version=%d method=%d attribs=%d comment=%d\n", header.flags, header.hostOs, header.fileCrc, header.unpackedVersion, header.method, header.attributes, header.commentSize);
		prynPrintFormat (state, "%r %u bytes", file.name, (unsigned int) file.size);
		
		if (file.archiveSize != file.size)
			prynPrintFormat (state, " (%u bytes in archive, %d%% compression)", (unsigned int) file.archiveSize, (unsigned int) (file.archiveSize * 100 / file.size));
		if (file.modifiedTime.year)
			prynPrintFormat (state, ", modified %t", file.modifiedTime);
		
		prynPrintLiteral (state, "\n");
		
		break;
	}
}

PrynResult load (PrynMediator *mediator, const char *filename)
{
	PrynState *state = prynMediatorState (mediator);
	HANDLE library = 0;
	PrynResult result = PrynResult_Success;
	TotalCommanderPlugin *plugin;
	PrynFactory *factory = 0;
	PrynString filenames = prynStringFromZ (filename);
	
	plugin = prynAllocateClearedObject (state, TotalCommanderPlugin);
	prynAssert (plugin, OutOfMemory);

	library = plugin->library = LoadLibraryA (filename);
	prynAssert (library, Failure);
	
	*(void **) &plugin->configurePacker = GetProcAddress (library, "ConfigurePacker");
	*(void **) &plugin->openArchive = GetProcAddress (library, "OpenArchive");
	*(void **) &plugin->readHeader = GetProcAddress (library, "ReadHeader");
	
	prynAssert (plugin->openArchive, Failure);
	prynAssert (plugin->readHeader, Failure);
	
	result = prynCreateFactory (&factory, prynMediatorLibrary (mediator), prynStringDuplicate (state, filenames));
	if (result < 0) goto failure;
	
	size_t filenameStart;
	for (filenameStart = filenames.length; filenameStart > 0; filenameStart --)
		if (filenames.pointer [filenameStart - 1] == '/' || filenames.pointer [filenameStart - 1] == '\\' || filenames.pointer [filenameStart - 1] == ':')
			break;
	
	prynFactorySetProprietary (factory, plugin);
	prynFactorySetMediator (factory, mediator);
	prynFactorySetName (factory, prynStringJoin (state, prynStringLiteral ("Total Commander Plugin - "), prynSliceStringToEnd (filenames, filenameStart)));
	prynFactorySetDescription (factory, prynStringLiteral ("Total Commander plugin for loading from and saving to archives."));
	prynFactorySetCreateFunction (factory, &totalCommanderFactoryCreate);
	
success:
	if (0)
	{
	failure:
		if (library) FreeLibrary (library);
	}
	
	return result;
}

PrynResult enable (PrynMediator *mediator)
{
	PrynState *state = prynMediatorState (mediator);
	
	SetErrorMode (0);
	
	//return load (mediator, "C:\\Program Files\\Winamp\\Plugins\\in_wave.dll");
	return load (mediator, "C:\\Program Files\\Total Commander\\plugins\\wcx\\AmigaDX\\AmigaDX.wcx");
	
	return PrynResult_Success;
}

PrynLibraryEntryPoint (PrynLibrary *library)
{
	PrynResult result = PrynResult_Success;
	PrynMediator *mediator;
	
	prynSetLibraryNameLiteral (library, "Total Commander plugin mediator");
	prynSetLibraryDescriptionLiteral (library, "This allows access to Ghistler Software's Total Commander plugins.");
	
	result = prynCreateMediator (library, &mediator, prynStringLiteral ("Pryn.TotalCommander"));
	if (result < 0) return result;
	
	prynSetMediatorNameLiteral (mediator, "Total Commander plugin");
	prynSetMediatorDescriptionLiteral (mediator, "This allows use of Ghistler Software's Total Commander plugins.");
	prynSetMediatorEnableFunction (mediator, &enable);
	
	return result;
}

